@tandryukha/aidemo 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,66 @@
1
+ ---
2
+ name: record-demo
3
+ description: >
4
+ Produce a short (30-60s) narrated, captioned product demo video with the aidemo
5
+ engine, via the aidemo MCP server. Use when the user says "record a demo of X",
6
+ "make a demo video", "demo this feature", or points at a flow (e.g. a shopping
7
+ flow inside a ChatGPT app) and wants a shareable MP4 with voiceover + captions.
8
+ The skill authors a storyboard.json (script + per-scene voice/music plan +
9
+ browser action-spec) and renders it with the engine. Do NOT use for: live
10
+ screen-sharing, editing an already-rendered MP4, or demos of native/mobile
11
+ apps (browser only).
12
+ allowed-tools: Bash, Read, Write, Edit, Glob, Grep
13
+ ---
14
+
15
+ # record-demo
16
+
17
+ You are the **demo director**. Turn a feature + loose directions into a polished
18
+ browser demo video using the `aidemo` engine.
19
+
20
+ This file is a thin adapter. **The authoring knowledge lives in the engine** —
21
+ fetch it first, then follow it:
22
+
23
+ 1. **Preferred — the `aidemo` MCP server** (registered by `aidemo repo-init`,
24
+ or bundled automatically if you installed the Claude Code plugin): call the
25
+ **`get_authoring_guide`** tool
26
+ and follow the guide. Pipeline operations (`probe`, `render`, `voice`,
27
+ `captions`, `compose`, `gif`) are **jobs**: they return a `jobId`
28
+ immediately — poll **`job_status`** for progress, results, and failure
29
+ artifacts. Validate storyboard edits with **`validate_storyboard`** before
30
+ rendering. Pass **absolute** demo directories.
31
+ 2. **Fallback — no MCP server registered**: run `aidemo guide` and follow the
32
+ same document using the CLI commands it maps out.
33
+ 3. **After verifying the final video** (per the guide's "Verify before
34
+ declaring done"): if you hit any engine bug, surprise, or workaround this
35
+ session, file it now — the MCP **`feedback`** tool (`title` + `body`;
36
+ environment/log context is auto-attached) or `aidemo feedback <dir>` on the
37
+ CLI (falls back to a local `docs/feedback-*.md` offline). If nothing came
38
+ up, skip this step.
39
+
40
+ ## Invoking the engine (`aidemo`)
41
+
42
+ **`aidemo <cmd>`** means whichever of these applies:
43
+ - **In the aidemo repo itself** (a full checkout): `node bin/aidemo.mjs <cmd>`.
44
+ - **In a product repo** where this skill was installed via `aidemo repo-init`
45
+ (the engine is *not* vendored here): `npx -y github:tandryukha/aidemo#stable <cmd>`.
46
+
47
+ If unsure, run `aidemo doctor` (or the MCP `doctor` tool) — it prints the
48
+ engine version and checks prereqs (ffmpeg, Chrome, `OPENAI_API_KEY` — or
49
+ `OPENAI_BASE_URL` pointing at a local OpenAI-compatible server, or
50
+ `AIDEMO_TTS_PROVIDER=local` for in-process TTS + offline captions; in both
51
+ cases no key is needed and doctor reports what's in effect).
52
+
53
+ ## Distribution & updates
54
+
55
+ This skill is a **versioned copy** of the source in the
56
+ [aidemo](https://github.com/tandryukha/aidemo) repo. In a product repo
57
+ it arrived via `aidemo repo-init`, which also added a `SessionStart` hook that
58
+ runs `aidemo skill check` and prints a notice when a newer version is out.
59
+ (The authoring guide itself never goes stale — it is served by the engine at
60
+ render time.)
61
+ - **Update the skill** (when notified, or any time): `aidemo skill update --dir .`
62
+ — rewrites this file and bumps `.claude/skills/record-demo/installed.json`.
63
+ Installed as a Claude Code plugin instead? Update it with `/plugin` (the
64
+ marketplace flow), not `aidemo skill update`.
65
+ - **Send feedback upstream**: see step 3 above.
66
+ - **Bootstrap a new repo**: `npx -y github:tandryukha/aidemo#stable repo-init --dir .`
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Andrii Taran
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,292 @@
1
+ # aidemo — your coding agent makes the demo video
2
+
3
+ **[aidemo.top](https://aidemo.top)** · [watch a real 51s output ▶](https://github.com/tandryukha/aidemo/releases/download/v0.3.0/wikipedia-showcase-demo.mp4) · [authoring guide](docs/AUTHORING.md) · [render in CI](docs/CI.md)
4
+
5
+ Tell your coding agent *"record a 45s demo of the checkout flow"* — get back a
6
+ polished **MP4 with voiceover, synced captions, and auto-zoom**. Any MCP-capable
7
+ agent (Claude Code, Codex CLI, Gemini CLI) writes one `storyboard.json`; the
8
+ headless engine drives a real Chrome, records a **deterministic replay**, voices
9
+ it, captions it, and trims the dead time. Because the replay is deterministic,
10
+ **the demo re-renders itself in CI** when the product changes — no re-recording,
11
+ no API key, about **$0 a render**. An **open-source (MIT) alternative to Screen
12
+ Studio, Clueso, or Demosmith** for when you'd rather your coding agent make the
13
+ demo.
14
+
15
+ [![Website](https://img.shields.io/badge/website-aidemo.top-1a7f37)](https://aidemo.top)
16
+ [![ci](https://github.com/tandryukha/aidemo/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/tandryukha/aidemo/actions/workflows/ci.yml)
17
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
18
+ [![Release](https://img.shields.io/github/v/release/tandryukha/aidemo)](https://github.com/tandryukha/aidemo/releases)
19
+ [![GitHub Marketplace](https://img.shields.io/badge/Marketplace-aidemo%20Demo%20Video-2ea44f?logo=githubactions&logoColor=white)](https://github.com/marketplace/actions/aidemo-demo-video)
20
+ [![Works with Claude Code](https://img.shields.io/badge/works%20with-Claude%20Code-d97757)](#three-ways-to-use-it)
21
+ [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/tandryukha/aidemo/badge)](https://scorecard.dev/viewer/?uri=github.com/tandryukha/aidemo)
22
+
23
+ **Install:** `/plugin marketplace add tandryukha/aidemo` (Claude Code) · `uses: tandryukha/aidemo@stable` (CI) · `npx -y github:tandryukha/aidemo#stable` (CLI / any agent)
24
+
25
+ [![aidemo demoing itself on Wikipedia — recorded with aidemo](docs/demo.gif)](https://github.com/tandryukha/aidemo/releases/download/v0.3.0/wikipedia-showcase-demo.mp4)
26
+
27
+ <sub>Real output — a ~51 s self-narrated tour of Wikipedia (portal search →
28
+ Ada Lovelace → focus-zoom → click through to the Analytical Engine → glide
29
+ scroll), authored by Claude from one `storyboard.json` and recorded as a
30
+ deterministic replay. The preview GIF is silent;
31
+ **[watch the full version with narration ▶](https://github.com/tandryukha/aidemo/releases/download/v0.3.0/wikipedia-showcase-demo.mp4)**.</sub>
32
+
33
+ ## Three ways to use it
34
+
35
+ 1. **From your coding agent** — the fastest path. In **Claude Code**:
36
+ `/plugin marketplace add tandryukha/aidemo` then
37
+ `/plugin install record-demo@aidemo` (bundles the skill **and** the MCP
38
+ server). In **Codex / Gemini / any MCP agent**:
39
+ `npx -y github:tandryukha/aidemo#stable repo-init`. Then just say
40
+ *"record a 45s demo of &lt;flow&gt;"* and the agent authors + renders it.
41
+ 2. **Locally, free & offline** —
42
+ `AIDEMO_TTS_PROVIDER=local aidemo render <dir> --headless`. An in-process
43
+ voice model + script-timed captions mean **no API key, ~$0, fully offline**.
44
+ See [docs/LOCAL_MODELS.md](docs/LOCAL_MODELS.md).
45
+ 3. **In CI, self-maintaining** — drop `uses: tandryukha/aidemo@stable` into a
46
+ workflow. When the product changes, it replays the committed storyboard and
47
+ commits fresh media — **no key, no LLM tokens, ~$0** on free runner minutes.
48
+ See [docs/CI.md](docs/CI.md).
49
+
50
+ **Free & open source.** MIT-licensed. Runs on GitHub's free Actions tier, or
51
+ fully local at $0 with the in-process voice — no API key, no cloud upload, no
52
+ telemetry. Works against **localhost and auth-walled apps** (your own Chrome).
53
+
54
+ ## From one sentence to a narrated MP4
55
+
56
+ You type a sentence, the agent writes an artifact you can read and edit, the
57
+ engine records and cuts it:
58
+
59
+ **1 · What you type** — one line to any MCP-capable agent (Claude Code here):
60
+
61
+ ```
62
+ claude "record a 45s demo touring Wikipedia: search for Ada Lovelace,
63
+ open the Analytical Engine, then glide down the article"
64
+ ```
65
+
66
+ **2 · What the agent authors** — a plain, editable `storyboard.json` (excerpt:
67
+ narration + a fixed browser action-spec, side by side — no generated code):
68
+
69
+ ```jsonc
70
+ {
71
+ "title": "A quick tour of Wikipedia",
72
+ "zoom": {}, // Screen-Studio-style auto-zoom
73
+ "scenes": [
74
+ { "id": "search",
75
+ "narration": "Start at the Wikipedia portal and search for Ada Lovelace.",
76
+ "actions": [
77
+ { "op": "goto", "url": "https://www.wikipedia.org/" },
78
+ { "op": "type", "target": { "selector": "#searchInput" }, "text": "Ada Lovelace" },
79
+ { "op": "click", "target": { "selector": "button[type=submit]" } } ] },
80
+ { "id": "engine",
81
+ "narration": "Her notes on Babbage's Analytical Engine hold the first computer program.",
82
+ "actions": [
83
+ { "op": "focus", "target": { "selector": "#firstHeading" } },
84
+ { "op": "click", "target": { "selector": "a[href*='Analytical_Engine']" } },
85
+ { "op": "scrollBy", "dy": 900, "easing": "glide" } ] }
86
+ ]
87
+ }
88
+ ```
89
+
90
+ **3 · How it's recorded + cut** — `aidemo render` drives a real Chrome (smooth
91
+ animated cursor, human-cadence typing, auto-zoom), then trims the dead time and
92
+ syncs to the narration:
93
+
94
+ ```
95
+ storyboard.json
96
+ → voice OpenAI / ElevenLabs / local TTS → audio/narration.mp3 + voice.json
97
+ → record drives Chrome, animated cursor → recordings/raw.{webm,mp4} + timeline.json
98
+ → captions Whisper word timestamps → generated/captions.{srt,vtt,cues.json}
99
+ → compose trim idle · sync · auto-zoom · cards · caption · mux → output/final-demo.mp4
100
+ ```
101
+
102
+ **4 · What you get** — `output/final-demo.mp4`, plus a README-ready GIF
103
+ (`aidemo gif`) and named stills (`aidemo stills`) from the same take. UI changed?
104
+ Re-run against the same storyboard — no re-recording by hand.
105
+
106
+ The design goal: demos that look **human-made and snappy**, not like an AI
107
+ clicking around and waiting between screenshots — by separating **authoring**
108
+ (slow, one-time — figure out the flow) from **recording** (a fast deterministic
109
+ replay with a smooth animated cursor).
110
+
111
+ ## What teams render with it
112
+
113
+ - **GitHub README demos** — `aidemo gif demos/onboarding`, drop the autoplaying
114
+ GIF into the readme (the GIFs on this page are exactly that).
115
+ - **Landing-page hero videos** — the muted-autoplay MP4 on
116
+ [aidemo.top](https://aidemo.top) is a rendered demo, poster frame and all.
117
+ - **Release / what-shipped demos** — narrate the new feature, then
118
+ `gh release upload v1.4.0 demos/whats-new/output/final-demo.mp4`.
119
+ - **Customer & prospect demos** — personalized flows against your real app:
120
+ localhost, auth walls, your own logged-in Chrome; nothing leaves the machine.
121
+
122
+ ## Render in CI (self-maintaining demos)
123
+
124
+ Commit a storyboard and the **[aidemo GitHub Action](docs/CI.md)** keeps its
125
+ demo media in sync — a fresh narrated, captioned MP4 (and GIF) on every relevant
126
+ change. Deterministic replay + a local voice mean **no API key, no LLM tokens,
127
+ about $0** (just free runner minutes). It's the loop a screen recorder can't
128
+ run: your demo maintains itself.
129
+
130
+ ```yaml
131
+ # .github/workflows/demo.yml
132
+ - uses: actions/checkout@v4
133
+ - run: sudo apt-get update && sudo apt-get install -y ffmpeg # ubuntu ships Chrome, not ffmpeg
134
+ - uses: tandryukha/aidemo@stable
135
+ with:
136
+ demos: demos/*
137
+ tts: local # in-process voice → no keys, no tokens
138
+ gif: "true"
139
+ ```
140
+
141
+ Full recipe, templates (auto-commit / PR-comment / cron-refresh), and the
142
+ always-fresh-embeds trick: **[docs/CI.md](docs/CI.md)**,
143
+ **[docs/EMBEDS.md](docs/EMBEDS.md)**, **[docs/recipes/](docs/recipes/)**.
144
+
145
+ ## Quick start (self-contained smoke test)
146
+
147
+ A bundled fixture store (search → results → cart → checkout) that renders a
148
+ finished demo with zero external dependencies:
149
+
150
+ ```bash
151
+ npm install # Node 20+, system Chrome, ffmpeg on PATH
152
+ node examples/local-demo/serve.mjs # terminal 1: fixture on :8787
153
+ node bin/aidemo.mjs render examples/local-demo --headless # terminal 2
154
+ open examples/local-demo/output/final-demo.mp4 # xdg-open on Linux, start on Windows
155
+ ```
156
+
157
+ Voice/captions need `OPENAI_API_KEY` in `.env` — **or** `AIDEMO_TTS_PROVIDER=local`
158
+ (no key, offline), **or** `OPENAI_BASE_URL` at a local server. See
159
+ [docs/LOCAL_MODELS.md](docs/LOCAL_MODELS.md). No Playwright browser download is
160
+ needed — the engine uses your system Chrome (`channel: "chrome"`).
161
+
162
+ [![Quickstart output — the bundled fixture rendered end-to-end](docs/quickstart.gif)](https://github.com/tandryukha/aidemo/releases/download/v0.3.0/quickstart-demo.mp4)
163
+
164
+ <sub>The bundled fixture rendered end-to-end — narrated, captioned, auto-trimmed.
165
+ Silent preview; **[full version ▶](https://github.com/tandryukha/aidemo/releases/download/v0.3.0/quickstart-demo.mp4)**.</sub>
166
+
167
+ ## CLI
168
+
169
+ Each step is independently runnable and re-runnable — regenerate voice without
170
+ re-recording, recompose without re-transcribing, etc.
171
+
172
+ ```bash
173
+ aidemo init <name> # scaffold demos/<name>/ with a starter storyboard
174
+ aidemo voice <dir> # per-scene TTS → narration.mp3 + voice.json
175
+ aidemo record <dir> # drive Chrome → raw video + timeline.json
176
+ aidemo probe <dir> # record-only dry run (verify selectors), no key needed
177
+ aidemo captions <dir> # Whisper → captions.{srt,vtt,cues.json} (--offline for no network)
178
+ aidemo compose <dir> # trim + sync + zoom + cards + caption + mux → final-demo.mp4
179
+ aidemo gif <dir> # final-demo.mp4 → README-ready GIF (autoplays on GitHub)
180
+ aidemo render <dir> # voice → record → captions → compose
181
+ aidemo guide # print the canonical authoring guide
182
+ aidemo doctor # check Node, ffmpeg, Chrome, voice endpoint
183
+ ```
184
+
185
+ Add `--headless` for CI/fixtures; omit it for real sites that need your
186
+ logged-in session. `--profile <dir>` picks the Chrome user-data dir;
187
+ `--capture native|obs` switches to high-fidelity screen capture. `voice`/`render`
188
+ **skip TTS for unchanged scenes**, and `record` **salvages a failed take** (keeps
189
+ the footage + drops a screenshot/frame-dump in `logs/`).
190
+
191
+ ## Agent interface (MCP)
192
+
193
+ `aidemo mcp` runs a **stdio** MCP server (no network listener) exposing the engine
194
+ to any MCP client. The Claude Code plugin bundles it; `aidemo repo-init` registers
195
+ it agent-neutrally (`.mcp.json` for Claude Code, `.gemini/settings.json` for
196
+ Gemini; `codex mcp add aidemo -- npx -y github:tandryukha/aidemo#stable mcp` for
197
+ Codex).
198
+
199
+ - **Authoring tools** — `get_authoring_guide` serves
200
+ [docs/AUTHORING.md](docs/AUTHORING.md) version-matched from the engine (can't go
201
+ stale); `get_storyboard_schema`, `validate_storyboard`, `init_demo`, `doctor`.
202
+ - **Pipeline tools run as jobs** — `probe`/`record`/`render`/`voice`/`captions`/
203
+ `compose`/`gif` return a `jobId` immediately; `job_status` reports stage,
204
+ per-scene progress, and (on failure) the screenshot/frame-dump paths.
205
+
206
+ ## Why it's built this way
207
+
208
+ - **Deterministic replay, not an LLM in the loop.** The recording runs a fixed
209
+ action-spec at full speed, so the video is smooth. The agent only *authors* the
210
+ storyboard (and confirms selectors once), never during capture.
211
+ - **Declarative action-spec + fixed player** (not generated `spec.ts`). Safer,
212
+ editable, and it emits a **timeline** for free — compose fits each scene's video
213
+ to its narration by trimming/speeding only the idle parts, freeze-holding a
214
+ static page for any remainder instead of ugly slow-motion.
215
+ - **Captions via overlaid PNGs, not libass.** Many ffmpeg builds lack
216
+ `subtitles`/`drawtext`; aidemo rasterizes each caption with headless Chrome and
217
+ overlays it with time-gated `enable` — works on any ffmpeg with `overlay`.
218
+ - **Cinematic polish is compose-time, not record-time** — a bad zoom is a
219
+ recompose, never a re-record. See [docs/POLISH.md](docs/POLISH.md).
220
+
221
+ ## Deeper docs
222
+
223
+ - **[docs/AUTHORING.md](docs/AUTHORING.md)** — the canonical storyboard schema,
224
+ action vocabulary, and demo-director principles (served by the engine).
225
+ - **[docs/LOCAL_MODELS.md](docs/LOCAL_MODELS.md)** — no-key rendering: in-process
226
+ Kokoro voice, local speech servers (speaches), ElevenLabs, offline captions.
227
+ - **[docs/POLISH.md](docs/POLISH.md)** — auto-zoom, scroll easing, music ducking,
228
+ intro/outro cards, motion blur, cursor control, native/OBS high-fidelity capture.
229
+ - **[docs/CHATGPT_APPS.md](docs/CHATGPT_APPS.md)** — recording ChatGPT Apps SDK
230
+ widgets (dedicated profile, nested iframes, `waitForWidget`).
231
+ - **[docs/CI.md](docs/CI.md)** · **[docs/EMBEDS.md](docs/EMBEDS.md)** · **[docs/recipes/](docs/recipes/)** — CI rendering, always-fresh embeds, agent-in-CI recipes.
232
+
233
+ ## Setup
234
+
235
+ Prereqs: **Node 20+**, **Google Chrome**, **ffmpeg + ffprobe** on `PATH`.
236
+ Developed and tested on macOS; Linux works for the default (Playwright) capture
237
+ and `--capture obs`. Run `aidemo doctor` to check your setup.
238
+
239
+ ```bash
240
+ npm install
241
+ cp .env.example .env # add OPENAI_API_KEY, or use AIDEMO_TTS_PROVIDER=local (no key)
242
+ ```
243
+
244
+ ## Project layout (per demo)
245
+
246
+ ```
247
+ demos/<name>/ ← your working area (untracked; scaffold with `aidemo init`)
248
+ input/ brief.md
249
+ generated/ storyboard.json timeline.json captions.{srt,vtt,cues.json}
250
+ recordings/ raw.webm (or raw.mp4 for native/OBS capture)
251
+ audio/ scene-*.mp3 narration.mp3 voice.json
252
+ output/ final-demo.mp4
253
+ logs/ <command>.log fail-<scene>-<n>.{png,json} (on a failed action)
254
+ ```
255
+
256
+ ## Security & trust
257
+
258
+ - **No telemetry, no analytics, no install-time scripts** (`package.json` has no
259
+ `postinstall`/`preinstall`).
260
+ - **Network is user-initiated only:** `api.openai.com` (only `voice`/`captions`,
261
+ your key — or a local server via `OPENAI_BASE_URL`), `api.elevenlabs.io` (opt-in),
262
+ `huggingface.co` (download-only, once, for `AIDEMO_TTS_PROVIDER=local`), and
263
+ `github.com` (your own `gh`, for `aidemo feedback`). Recording/composing are
264
+ fully local. The MCP server is **stdio-only** — no listener.
265
+ - **Small, auditable surface:** ~20 source files, 7 runtime deps, MIT. Pin an
266
+ immutable ref if you're wary of the moving `#stable` tag:
267
+ `npx -y github:tandryukha/aidemo#v0.8.0`.
268
+ - Full detail: [docs/LOCAL_MODELS.md](docs/LOCAL_MODELS.md#what-leaves-the-machine)
269
+ · report vulnerabilities privately per [SECURITY.md](SECURITY.md).
270
+
271
+ ## Roadmap
272
+
273
+ - **Comments on the video** (pause & comment) and **in-place transcript editing**:
274
+ captions map to scenes, so editing a line marks that scene dirty and
275
+ `aidemo voice --scene <id>` + `compose` regenerates only the delta.
276
+ - **Web UI**, project history, brand kits, changelog integrations.
277
+ - **Hosted public MCP** (see [docs/plans/public-mcp.md](docs/plans/public-mcp.md)).
278
+
279
+ Shipped: the **GitHub Action** (CI re-render), cinematic polish (auto-zoom,
280
+ scroll easing, music ducking, intro/outro cards, motion blur, post-hoc cursor
281
+ control), native/OBS capture, the agent-neutral **MCP server** + authoring guide,
282
+ ElevenLabs and **in-process local voice** providers, and the **Claude Code plugin**.
283
+
284
+ ## Contributing
285
+
286
+ Issues and PRs welcome — see [CONTRIBUTING.md](CONTRIBUTING.md) for dev setup, the
287
+ smoke test, and the DCO sign-off requirement. Recording-session feedback has a fast
288
+ path: `aidemo feedback demos/<name>` pre-fills a structured issue.
289
+
290
+ ## License
291
+
292
+ [MIT](LICENSE) © Andrii Taran
package/bin/aidemo.mjs ADDED
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+ // Thin launcher so `aidemo` runs the TypeScript CLI via tsx without a build step.
3
+ import { spawnSync } from "node:child_process";
4
+ import { fileURLToPath } from "node:url";
5
+ import { createRequire } from "node:module";
6
+ import { existsSync } from "node:fs";
7
+ import { dirname, resolve } from "node:path";
8
+
9
+ const here = dirname(fileURLToPath(import.meta.url));
10
+ const cli = resolve(here, "..", "src", "cli.ts");
11
+ const require = createRequire(import.meta.url);
12
+
13
+ /**
14
+ * Resolve tsx's CLI entry via Node's module resolution so it works both from a
15
+ * local checkout (tsx under the repo's node_modules) AND from a hoisted
16
+ * `npx github:tandryukha/aidemo@stable` install, where npm lifts tsx into a
17
+ * parent node_modules that `<pkg>/node_modules/.bin/tsx` would miss.
18
+ */
19
+ function findTsxBin() {
20
+ try {
21
+ // Walk up from tsx's resolved entry to its package.json, then read its bin.
22
+ let dir = dirname(require.resolve("tsx"));
23
+ for (let i = 0; i < 6 && dir !== dirname(dir); i++) {
24
+ const pj = resolve(dir, "package.json");
25
+ if (existsSync(pj)) {
26
+ const pkg = require(pj);
27
+ if (pkg.name === "tsx" && pkg.bin) {
28
+ const binRel = typeof pkg.bin === "string" ? pkg.bin : pkg.bin.tsx;
29
+ if (binRel) return resolve(dir, binRel);
30
+ }
31
+ }
32
+ dir = dirname(dir);
33
+ }
34
+ } catch {
35
+ /* fall through to the local .bin guess */
36
+ }
37
+ const local = resolve(here, "..", "node_modules", ".bin", "tsx");
38
+ return existsSync(local) ? local : null;
39
+ }
40
+
41
+ const tsxBin = findTsxBin();
42
+ if (!tsxBin) {
43
+ console.error(
44
+ "aidemo: could not locate the 'tsx' runtime. Run `npm install` in the engine, " +
45
+ "or invoke via `npx -y github:tandryukha/aidemo@stable <cmd>`."
46
+ );
47
+ process.exit(1);
48
+ }
49
+
50
+ const res = spawnSync(process.execPath, [tsxBin, cli, ...process.argv.slice(2)], {
51
+ stdio: "inherit",
52
+ });
53
+ process.exit(res.status ?? 1);